#include "linux/kernel.h"
#include "lib/vsprintf.h"
#include "lib/string.h"
#include "linux/errno.h"

/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */

/*
 *      Array of consoles built from command line options (console=)
 */
struct console_cmdline
{
  char    name[8];                        /* Name of the driver       */
  int     index;                          /* Minor dev. to use        */
  char    *options;                       /* Options for the driver   */
};

#define MAX_CMDLINECONSOLES 8

static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
static int selected_console = -1;
static int preferred_console = -1;


static void emit_log_char(char c) {  
}

/*
 *      Setup a list of consoles. Called from init/main.c
 */
static int __init console_setup(char *str)
{
  char name[sizeof(console_cmdline[0].name)];
  char *s, *options;
  int idx;

  /*
   *      Decode str into name, index, options.
   */
  if (str[0] >= '0' && str[0] <= '9') {
    strcpy(name, "ttyS");
    strncpy(name + 4, str, sizeof(name) - 5);
  } else {
    strncpy(name, str, sizeof(name) - 1);
  }
  
  name[sizeof(name) - 1] = 0;
  if ((options = strchr(str, ',')) != NULL)
    *(options++) = 0;

  for(s = name; *s; s++) {
    if ((*s >= '0' && *s <= '9') || *s == ',') {
      break;
    }
  }
  idx = simple_strtoul(s, NULL, 10);
  *s = 0;

  add_preferred_console(name, idx, options);
  return 1;
}


/**
 * add_preferred_console - add a device to the list of preferred consoles.
 *
 * The last preferred console added will be used for kernel messages
 * and stdin/out/err for init.  Normally this is used by console_setup
 * above to handle user-supplied console arguments; however it can also
 * be used by arch-specific code either to override the user or more
 * commonly to provide a default console (ie from PROM variables) when
 * the user has not supplied one.
 */
int __init add_preferred_console(char *name, int idx, char *options) {
  struct console_cmdline *c;
  int i;

  /*
   *      See if this tty is not yet registered, and
   *      if we have a slot free.
   */
  for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
    if (strcmp(console_cmdline[i].name, name) == 0 &&
        console_cmdline[i].index == idx) {
      selected_console = i;
      return 0;
    }
  if (i == MAX_CMDLINECONSOLES)
    return -E2BIG;
  selected_console = i;
  c = &console_cmdline[i];
  memcpy(c->name, name, sizeof(c->name));
  c->name[sizeof(c->name) - 1] = 0;
  c->options = options;
  c->index = idx;
  return 0;
}

/**
 * This is printk.  It can be called from any context.  We want it to work.
 *
 * We try to grab the console_sem.  If we succeed, it's easy - we log the output and
 * call the console drivers.  If we fail to get the semaphore we place the output
 * into the log buffer and return.  The current holder of the console_sem will
 * notice the new output in release_console_sem() and will send it to the
 * consoles before releasing the semaphore.
 *
 * One effect of this deferred printing is that code which calls printk() and
 * then changes console_loglevel may break. This is because console_loglevel
 * is inspected when the actual printing occurs.
 */
asmlinkage int printk(const char *fmt, ...)
{
  va_list args;
  int r;

  va_start(args, fmt);
  r = vprintk(fmt, args);
  va_end(args);

  return r;
}

asmlinkage int vprintk(const char *fmt, va_list args) {
  // unsigned long flags;
  int printed_len;
  char *p;
  static char printk_buf[1024];
  static int log_level_unknown = 1;

  /*
  if (unlikely(oops_in_progress))
    zap_locks();
  */

  /* This stops the holder of console_sem just where we want him */
  // spin_lock_irqsave(&logbuf_lock, flags);

  /* Emit the output into the temporary buffer */
  printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);

  /*
   * Copy the output into log_buf.  If the caller didn't provide
   * appropriate log level tags, we insert them here
   */
  for (p = printk_buf; *p; p++) {
    if (log_level_unknown) {
      if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {
        emit_log_char('<');
        emit_log_char(DEFAULT_MESSAGE_LOGLEVEL + '0');
        emit_log_char('>');
      }
      log_level_unknown = 0;
    }
    emit_log_char(*p);
    if (*p == '\n')
      log_level_unknown = 1;
  }

  /*
  if (!cpu_online(smp_processor_id()) &&
      system_state != SYSTEM_RUNNING) {
    
    // Some console drivers may assume that per-cpu resources have
    // been allocated.  So don't allow them to be called by this
    // CPU until it is officially up.  We shouldn't be calling into
    // random console drivers on a CPU which doesn't exist yet..
     
    spin_unlock_irqrestore(&logbuf_lock, flags);
    goto out;
  }
  
  if (!down_trylock(&console_sem)) {
    console_locked = 1;

    // We own the drivers.  We can drop the spinlock and let
    // release_console_sem() print the text
    spin_unlock_irqrestore(&logbuf_lock, flags);
    console_may_schedule = 0;
    release_console_sem();
  } else {

    // Someone else owns the drivers.  We drop the spinlock, which
    // allows the semaphore holder to proceed and to call the
    // console drivers with the output which we just produced.
    spin_unlock_irqrestore(&logbuf_lock, flags);
  }

out:
  */
  return printed_len;
}
